﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Xml.Linq;
using LiveWebServiceBase.AOPLogWriter;

namespace LiveWebServiceBase.ServiceUserSettings
{
    public class Organization
    {
        public string orgname;
        public string sharedsecretkey;
        public string campus;
        public string department;
        public string defaultgroup;
        public string elmsurl;
        public string login;

        public List<SiteID> lstSiteIDs;

        public List<string> GetDomains()
        {
            return lstSiteIDs.SelectMany(le => le.GetDomains()).ToList();
        }
    }

    public class SiteID
    {
        public string value;
        public string path;
        public string pwd;
        public byte[] certfile;

        public SiteID() { }
        public SiteID(string _value, string _pwd, byte[] _certfile)
        {
            value = _value;
            pwd = _pwd;
            certfile = _certfile;
        }

        public List<Account> lstAccounts;

        public List<string> GetDomains()
        {

            return lstAccounts.SelectMany(le => le.lstDomains).ToList();
        }
    }

    public class Account
    {
        public int id;

        public string domainAdm;
        public string domainAdmPwd;

        public string ewsAdm;
        public string ewsAdmPwd;

        public string psAdm;
        public string psAdmPwd;
        public bool multiplesessions;

        public List<string> lstDomains;
    }

    public class UserSettings
    {
        private static object objLock = new object();

        private XDocument GetConfigXML()
        {
            lock (objLock)
            {
                XDocument doc = null;

                using (FileStream fsUserSettings = File.Open(_pathtouserconfigfile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Delete))
                {
                    doc = XDocument.Load(fsUserSettings);

                    if (doc.Root.IsEmpty)
                    {
                        doc.Add(new XElement("settings"));
                        doc.Save(fsUserSettings);
                    }
                }

                return doc;
            }
        }

        private void SetConfigXML(XDocument doc)
        {
            lock (objLock)
            {
                using (FileStream fsUserSettings = File.Open(_pathtouserconfigfile, FileMode.Append, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    doc.Save(fsUserSettings);
                }
            }
        }

        private XDocument _doc;
        private string _username;
        private string _fullusername
        {
            get
            {
                return string.Format(@"{0}\{1}", Environment.MachineName, _username).ToLower();
            }
        }
        private string _pathtouserconfigfile
        {
            get
            {
                return Settings.Paths.PathToConfigDir + "\\" + _username + "\\settings.xml";
            }
        }
        private string _pathtouserdir
        {
            get
            {
                return Settings.Paths.PathToConfigDir + "\\" + _username;
            }
        }


        public UserSettings(string username)
        {
            _username = Settings.UserInfo.GetUserName(username);
            _doc = GetConfigXML();
        }

        public void ReanmeUser(string newusename)
        {
            IEnumerable<XElement> userorgs = _doc.Root.Elements("organization");

            foreach (var item in userorgs)
            {
                item.Attribute("login").Value = Environment.MachineName + "\\" + newusename;
            }

            SetConfigXML(_doc);
        }

        #region Organizations
        public List<Organization> GetOrganizations()
        {
            List<Organization> lstOrg = new List<Organization> { };

            var organizations = _doc.Root.Elements("organization").Where(le => le.GetAttributeValue("login").ToLower() == _fullusername);

            if (organizations.Count() > 0)
            {
                foreach (var organization in organizations)
                {
                    if (organization.Attribute("sharedsecretkey") == null)
                    {
                        organization.Add(new XAttribute("sharedsecretkey", ""));
                    }

                    Organization org = new Organization();
                    org.orgname = organization.GetAttributeValue("value");
                    org.sharedsecretkey = organization.GetAttributeValue("sharedsecretkey");
                    org.campus = organization.GetAttributeValue("campus");
                    org.department = organization.GetAttributeValue("department");
                    org.defaultgroup = organization.GetAttributeValue("defaultgroup");
                    org.elmsurl = organization.GetAttributeValue("elmsurl");
                    org.login = organization.GetAttributeValue("login");

                    lstOrg.Add(org);
                }
            }

            return lstOrg;
        }

        public Organization GetOrganization(string orgname)
        {
            Organization org = new Organization();
            var organization = _doc.Root.Elements("organization").Single(le =>
                le.GetAttributeValue("login").ToLower().Contains(_username.ToLower()) &&
                le.GetAttributeValue("value").ToLower() == orgname.ToLower()
                );

            org.orgname = organization.GetAttributeValue("value");
            org.sharedsecretkey = organization.GetAttributeValue("sharedsecretkey");
            org.campus = organization.GetAttributeValue("campus");
            org.department = organization.GetAttributeValue("department");
            org.defaultgroup = organization.GetAttributeValue("defaultgroup");
            org.elmsurl = organization.GetAttributeValue("elmsurl");
            org.login = organization.GetAttributeValue("login");

            return org;
        }

        public void CreateOrganization(Organization org)
        {
            if (_doc.Root.Elements("organization").Where(le => le.GetAttributeValue("value").ToLower() == org.orgname.ToLower()).Count() > 0)
            {
                throw new Exception("Organization '" + org.orgname + "' already exists.");
            }

            XElement neworg = new XElement("organization");
            neworg.Add(new XAttribute("value", org.orgname),
                new XAttribute("sharedsecretkey", org.sharedsecretkey),
                new XAttribute("campus", org.campus),
                new XAttribute("department", org.department),
                new XAttribute("defaultgroup", org.defaultgroup),
                new XAttribute("elmsurl", org.elmsurl),
                new XAttribute("login", (_fullusername).ToLower())
                );
            _doc.Root.Add(neworg);

            SetConfigXML(_doc);
        }

        public void EditOrganization(string oldorgname, Organization neworg)
        {
            if (_doc.Root.Elements("organization").Where(le =>
                le.GetAttributeValue("value").ToLower() == neworg.orgname.ToLower()).Count() > 0 && oldorgname != neworg.orgname)
            {
                throw new Exception("Organization '" + neworg.orgname + "' already exists.");
            }

            XElement xoldorg = _doc.Descendants("organization").Single(le => le.GetAttributeValue("value").ToLower() == oldorgname.ToLower());
            xoldorg.Attribute("value").Value = neworg.orgname;

            if (xoldorg.Attribute("sharedsecretkey") == null)
            {
                xoldorg.Add(new XAttribute("sharedsecretkey", ""));
            }

            xoldorg.Attribute("sharedsecretkey").Value = neworg.sharedsecretkey;
            xoldorg.Attribute("campus").Value = neworg.campus;
            xoldorg.Attribute("department").Value = neworg.department;
            xoldorg.Attribute("defaultgroup").Value = neworg.defaultgroup;
            xoldorg.Attribute("elmsurl").Value = neworg.elmsurl;

            SetConfigXML(_doc);
        }

        public void DeleteOrganization(string orgname)
        {
            XElement xorg = _doc.Descendants("organization").Single(le => le.GetAttributeValue("value").ToLower() == orgname.ToLower());

            var CertificatePaths = xorg.Descendants("siteid").Select(le => le.GetAttributeValue("path"));

            foreach (string item in CertificatePaths)
            {
                File.Delete(item);
            }

            xorg.Remove();

            SetConfigXML(_doc);
        }
        #endregion

        #region SiteIDs
        public List<SiteID> GetSiteIDs(string orgname)
        {
            List<SiteID> lstSiteID = new List<SiteID> { };

            var siteids = _doc.Descendants("siteid").Where(le =>
                le.Parent.GetAttributeValue("login").ToLower().Contains(_username.ToLower()) &&
                le.Parent.GetAttributeValue("value") == orgname);

            foreach (var site in siteids)
            {
                SiteID siteid = new SiteID();
                siteid.value = site.GetAttributeValue("value");
                siteid.path = site.GetAttributeValue("path");
                siteid.pwd = site.GetAttributeValue("pwd");

                siteid.certfile = File.ReadAllBytes(siteid.path);

                lstSiteID.Add(siteid);
            }

            return lstSiteID;
        }

        public void CreateSiteID(string orgname, SiteID siteid)
        {
            if (_doc.Descendants("siteid").Count(le =>
                le.GetAttributeValue("value") == siteid.value &&
                le.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()) == 1)
            {
                throw new Exception("Site '" + orgname + "' already exists.");
            }

            XElement newsiteid = new XElement("siteid");
            newsiteid.Add(
                new XAttribute("value", siteid.value),
                new XAttribute("path", _pathtouserdir + "\\" + siteid.value + ".pfx"),
                new XAttribute("pwd", siteid.pwd));

            if (!string.IsNullOrEmpty(siteid.pwd))
                try
                {
                    X509Certificate cert = new X509Certificate(siteid.certfile, siteid.pwd);
                    cert = null;
                }
                catch
                {
                    throw new Exception("Incorrect certificate password.");
                }

            using (FileStream fstream = File.Create(_pathtouserdir + "\\" + siteid.value + ".pfx"))
            {
                fstream.Write(siteid.certfile, 0, siteid.certfile.Length);
                fstream.Flush();
            }

            XElement org = _doc.Descendants("organization").Single(le => le.GetAttributeValue("value").ToLower() == orgname.ToLower());
            org.Add(newsiteid);

            SetConfigXML(_doc);
        }

        public void EditSiteID(string orgname, string oldsiteid, SiteID newsiteid)
        {
            if (oldsiteid != newsiteid.value && _doc.Descendants("siteid").Count(le =>
            le.GetAttributeValue("value") == newsiteid.value &&
            le.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()) == 1)
            {
                throw new Exception("Site '" + newsiteid.value + "' already exists.");
            }

            XElement oldsite = _doc.Descendants("siteid").Single(le =>
                le.GetAttributeValue("value") == oldsiteid &&
                le.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower());

            oldsite.Attribute("value").Value = newsiteid.value;
            oldsite.Attribute("path").Value = _pathtouserdir + "\\" + newsiteid.value + ".pfx";
            oldsite.Attribute("pwd").Value = newsiteid.pwd;

            if (!string.IsNullOrEmpty(newsiteid.pwd))
                try
                {
                    X509Certificate cert = new X509Certificate(newsiteid.certfile, newsiteid.pwd);
                    cert = null;
                }
                catch
                {
                    throw new Exception("Incorrect certificate password.");
                }

            File.Delete(_pathtouserdir + "\\" + oldsiteid + ".pfx");

            using (FileStream fstream = File.Create(_pathtouserdir + "\\" + newsiteid.value + ".pfx"))
            {
                fstream.Write(newsiteid.certfile, 0, newsiteid.certfile.Length);
                fstream.Flush();
            }

            SetConfigXML(_doc);
        }

        public void DeleteSiteID(string orgname, string siteid)
        {
            XElement site = _doc.Descendants("siteid").Single(le =>
                le.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower() &&
                le.GetAttributeValue("value") == siteid);

            string pathtocert = site.GetAttributeValue("path");

            File.Delete(pathtocert);

            site.Remove();

            SetConfigXML(_doc);
        }
        #endregion

        #region Accounts
        public List<Account> GetAccounts(string siteid)
        {
            List<Account> lstAcnt = new List<Account> { };

            var Account = _doc.Root.Descendants("account").Where(le =>
                le.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.GetAttributeValue("login").ToLower().Contains(_username.ToLower())
                );

            foreach (var account in Account)
            {
                Account acc = new Account();

                acc.id = Convert.ToInt32(account.GetAttributeValue("id"));

                acc.domainAdm = account.GetAttributeValue("dmid");
                acc.domainAdmPwd = account.GetAttributeValue("dmpwd");

                acc.ewsAdm = account.GetAttributeValue("exid");
                acc.ewsAdmPwd = account.GetAttributeValue("expwd");

                acc.psAdm = account.GetAttributeValue("psid");
                acc.psAdmPwd = account.GetAttributeValue("pspwd");

                acc.multiplesessions = account.GetAttributeBoolValue("multiplesessions");

                lstAcnt.Add(acc);
            }

            return lstAcnt;
        }

        public Account GetAccount(string siteid, string accountid)
        {
            Account Acnt = new Account();

            var account = _doc.Descendants("account").Single(le =>
                le.GetAttributeValue("id") == accountid &&
                le.Parent.GetAttributeValue("value") == siteid
                );

            Acnt.id = Convert.ToInt32(account.GetAttributeValue("id"));

            Acnt.domainAdm = account.GetAttributeValue("dmid");
            Acnt.domainAdmPwd = account.GetAttributeValue("dmpwd");

            Acnt.ewsAdm = account.GetAttributeValue("exid");
            Acnt.ewsAdmPwd = account.GetAttributeValue("expwd");

            Acnt.psAdm = account.GetAttributeValue("psid");
            Acnt.psAdmPwd = account.GetAttributeValue("pspwd");

            Acnt.multiplesessions = account.GetAttributeBoolValue("multiplesessions");

            return Acnt;
        }

        public void CreateAccount(string orgname, string siteid, Account Account)
        {
            if (_doc.Descendants("account").Count(le =>
                le.GetAttributeValue("dmid").ToLower() == Account.domainAdm.ToLower() &&
                le.GetAttributeValue("exid").ToLower() == Account.ewsAdm.ToLower() &&
                le.GetAttributeValue("psid").ToLower() == Account.psAdm.ToLower() &&
                le.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()
                ) == 1)
            {
                throw new Exception("Account '" + Account.domainAdm + "," + Account.ewsAdm + "," + Account.psAdm + "' already exists in " + orgname + "\\" + siteid);
            }

            List<string> IDs = _doc.Descendants("account").Where(le =>
                le.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()
                ).Select(le => le.GetAttributeValue("id")).ToList();

            XElement dom = new XElement("account");

            int id = 1;

            if (IDs != null && IDs.Count > 0)
                id = Convert.ToInt32(IDs.Max()) + 1;

            dom.Add(new XAttribute("id", id));

            dom.Add(new XAttribute("dmid", Account.domainAdm.ToLower()));
            dom.Add(new XAttribute("dmpwd", Account.domainAdmPwd));

            dom.Add(new XAttribute("exid", Account.ewsAdm.ToLower()));
            dom.Add(new XAttribute("expwd", Account.ewsAdmPwd));

            dom.Add(new XAttribute("psid", Account.psAdm.ToLower()));
            dom.Add(new XAttribute("pspwd", Account.psAdmPwd));

            dom.Add(new XAttribute("multiplesessions", false));

            XElement sid = _doc.Descendants("siteid").Single(le =>
                le.GetAttributeValue("value") == siteid &&
                le.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower());
            sid.Add(dom);

            SetConfigXML(_doc);
        }

        public void EditAccount(string orgname, string siteid, Account oldAccount, Account newAccount)
        {
            if (_doc.Descendants("account").Count(le =>
                le.GetAttributeValue("dmid").ToLower() == newAccount.domainAdm.ToLower() &&
                le.GetAttributeValue("exid").ToLower() == newAccount.ewsAdm.ToLower() &&
                le.GetAttributeValue("psid").ToLower() == newAccount.psAdm.ToLower() &&
                le.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()
                ) == 1)
            {
                throw new Exception("Account '" + newAccount.domainAdm + "," + newAccount.ewsAdm + "," + newAccount.psAdm + "' already exists in " + orgname + "\\" + siteid);
            }

            XElement dom = _doc.Descendants("account").Single(le =>
                le.GetAttributeValue("dmid") == oldAccount.domainAdm.ToLower() &&
                le.GetAttributeValue("exid") == oldAccount.ewsAdm.ToLower() &&
                le.GetAttributeValue("psid") == oldAccount.psAdm.ToLower()
                );

            dom.Attribute("dmid").Value = newAccount.domainAdm.ToLower();
            dom.Attribute("dmpwd").Value = newAccount.domainAdmPwd;

            dom.Attribute("exid").Value = newAccount.ewsAdm.ToLower();
            dom.Attribute("expwd").Value = newAccount.ewsAdmPwd;

            dom.Attribute("exid").Value = newAccount.psAdm.ToLower();
            dom.Attribute("expwd").Value = newAccount.psAdmPwd;

            XElement sid = _doc.Descendants("siteid").Single(le =>
                le.GetAttributeValue("value") == siteid &&
                le.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower());
            sid.Add(dom);

            SetConfigXML(_doc);
        }

        public void EditAccount(string orgname, string siteid, string accountid, Account newAccount)
        {
            if (_doc.Descendants("account").Count(le =>
                le.GetAttributeValue("id") != accountid &&
                le.GetAttributeValue("dmid").ToLower() == newAccount.domainAdm &&
                le.GetAttributeValue("exid").ToLower() == newAccount.ewsAdm &&
                le.GetAttributeValue("psid").ToLower() == newAccount.psAdm &&
                le.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()
            ) == 1)
                throw new Exception("Account '" + newAccount.domainAdm + "," + newAccount.ewsAdm + "," + newAccount.psAdm + "' already exists in " + orgname + "\\" + siteid);

            XElement oldaccount = _doc.Descendants("account").Single(le =>
                le.GetAttributeValue("id") == accountid &&
                le.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()
            );

            oldaccount.Attribute("dmid").Value = newAccount.domainAdm.ToLower();
            if (!string.IsNullOrEmpty(newAccount.domainAdmPwd))
                oldaccount.Attribute("dmpwd").Value = newAccount.domainAdmPwd;

            oldaccount.Attribute("exid").Value = newAccount.ewsAdm.ToLower();
            if (!string.IsNullOrEmpty(newAccount.ewsAdmPwd))
                oldaccount.Attribute("expwd").Value = newAccount.ewsAdmPwd;

            oldaccount.Attribute("psid").Value = newAccount.psAdm.ToLower();
            if (!string.IsNullOrEmpty(newAccount.psAdmPwd))
                oldaccount.Attribute("pspwd").Value = newAccount.psAdmPwd;

            SetConfigXML(_doc);
        }

        public void DeleteAccount(string orgname, string siteid, string accountid)
        {
            XElement Account = _doc.Descendants("account").Single(le =>
                le.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower() &&
                le.Parent.GetAttributeValue("value") == siteid &&
                le.GetAttributeValue("id") == accountid);

            Account.Remove();

            SetConfigXML(_doc);
        }
        #endregion

        #region Domains
        public List<string> GetDomains(Account Account)
        {
            List<string> lstDomains = new List<string> { };

            var domains = _doc.Descendants("domain").Where(le =>
                le.Parent.GetAttributeValue("dmid").ToLower() == Account.domainAdm.ToLower() &&
                le.Parent.GetAttributeValue("exid").ToLower() == Account.ewsAdm.ToLower() &&
                le.Parent.GetAttributeValue("psid").ToLower() == Account.psAdm.ToLower() &&
                le.Parent.Parent.Parent.GetAttributeValue("login").ToLower() == _fullusername
                );

            foreach (var domain in domains)
            {
                lstDomains.Add(domain.GetAttributeValue("value").ToLower());
            }

            return lstDomains;
        }

        public List<string> GetDomains(string accountid, string siteid, string orgname)
        {
            List<string> lstDomains = new List<string> { };

            var domains = _doc.Descendants("domain").Where(le =>
                le.Parent.GetAttributeValue("id") == accountid &&
                le.Parent.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower() &&
                le.Parent.Parent.Parent.GetAttributeValue("login").ToLower() == _fullusername
                );

            foreach (var domain in domains)
            {
                lstDomains.Add(domain.GetAttributeValue("value"));
            }

            return lstDomains;
        }

        public void CreateDomain(string orgname, string siteid, string accountid, string domname)
        {
            if (_doc.Descendants("domain").Count(le =>
                le.GetAttributeValue("value").ToLower() == domname.ToLower() &&
                le.Parent.GetAttributeValue("id") == accountid &&
                le.Parent.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()
                ) == 1)
            {
                throw new Exception("Domain '" + domname + "' already exists in " + orgname + "\\" + siteid + "\\" + GetAccount(siteid, accountid));
            }


            XElement dom = new XElement("domain");

            dom.Add(new XAttribute("value", domname));

            XElement sid = _doc.Descendants("account").Single(le =>
                le.GetAttributeValue("id") == accountid &&
                le.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower());
            sid.Add(dom);

            SetConfigXML(_doc);
        }

        public void EditDomain(string orgname, string siteid, string accountid, string olddomname, string newdomainname)
        {
            if (_doc.Descendants("domain").Count(le =>
                le.GetAttributeValue("value").ToLower() == newdomainname.ToLower() &&
                le.Parent.GetAttributeValue("id") == accountid &&
                le.Parent.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()
                ) == 1)
            {
                throw new Exception("Domain '" + newdomainname + "' already exists in " + orgname + "\\" + siteid + "\\" + GetAccount(siteid, accountid));
            }

            XElement dom = _doc.Descendants("domain").Single(le =>
                le.GetAttributeValue("value").ToLower() == olddomname.ToLower() &&
                le.Parent.GetAttributeValue("id") == accountid &&
                le.Parent.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower()
                );

            dom.Attribute("value").Value = newdomainname;

            SetConfigXML(_doc);
        }

        public void DeleteDomain(string orgname, string siteid, string accountid, string domainname)
        {
            XElement domain = _doc.Descendants("domain").Single(le =>
                le.Parent.Parent.Parent.GetAttributeValue("value").ToLower() == orgname.ToLower() &&
                le.Parent.Parent.GetAttributeValue("value") == siteid &&
                le.Parent.GetAttributeValue("id") == accountid &&
                le.GetAttributeValue("value").ToLower() == domainname.ToLower());

            domain.Remove();

            SetConfigXML(_doc);
        }
        #endregion
    }

    public static class XExtentions
    {
        public static string GetAttributeValue(this XElement element, string xname)
        {
            if (element.Attribute(xname) == null)
                return string.Empty;
            else
                return element.Attribute(xname).Value;
        }

        public static bool GetAttributeBoolValue(this XElement element, string xname)
        {
            try
            {
                if (element.Attribute(xname) == null)
                    return false;
                else
                    return Convert.ToBoolean(element.Attribute(xname).Value);
            }
            catch
            {
                return false;
            }
        }
    }
}
